/**
 * 
 */
package com.feizhi.common.repo;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author shuaqiu 2013年11月24日
 * 
 */
public class Condition {

    private final List<Clause> clauses = new ArrayList<Clause>();

    private List<String> custom = null;

    private Map<String, Object> customParam = null;

    /**
     * 获取添加的查询条件
     * 
     * @return
     */
    public List<Clause> getClauses() {
        return clauses;
    }

    /**
     * 获取添加的自定义的条件条件
     * 
     * @return the custom
     */
    public List<String> getCustom() {
        return custom;
    }

    /**
     * 获取添加的自定义参数
     * 
     * @return
     */
    public Map<String, Object> getCustomParam() {
        return customParam;
    }

    /**
     * 是否没有添加任何查询条件
     * 
     * @return
     */
    public boolean isEmpty() {
        return clauses.size() == 0 && (custom == null || custom.size() == 0)
                && (customParam == null || customParam.size() == 0);
    }

    /**
     * Like 条件, 根据这个条件组装语句为: <br/>
     * (props[0] like '%value%' or props[1] like '%value%' or ...)
     * 
     * @param value
     *            值
     * @param props
     *            字段属性列表, 可以是多个, 这几个字段之间是or 的关系
     * @return
     */
    public Condition addLike(final String value, final String... props) {
        if (value != null) {
            clauses.add(new Clause(value, props, Type.LIKE));
        }
        return this;
    }

    /**
     * Not Like 条件, 与Like 相反, 根据这个条件组装语句为: <br/>
     * (props[0] not like '%value%' or props[1] not like '%value%' or ...)
     * 
     * @param value
     *            值
     * @param props
     *            字段属性列表, 可以是多个, 这几个字段之间是or 的关系
     * @return
     */
    public Condition addNotLike(final String value, final String... props) {
        if (value != null) {
            clauses.add(new Clause(value, props, Type.NOT_LIKE));
        }
        return this;
    }

    /**
     * = 条件, 根据这个条件组装语句为: <br/>
     * (props[0] = value or props[1] = value or ...)
     * 
     * @param value
     *            值
     * @param props
     *            字段属性列表, 可以是多个, 这几个字段之间是or 的关系
     * @return
     */
    public Condition addEquals(final Object value, final String... props) {
        clauses.add(new Clause(value, props, Type.EQUALS));
        return this;
    }

    /**
     * != 条件, 根据这个条件组装语句为: <br/>
     * (props[0] != value or props[1] != value or ...)
     * 
     * @param value
     *            值
     * @param props
     *            字段属性列表, 可以是多个, 这几个字段之间是or 的关系
     * @return
     */
    public Condition addNotEquals(final Object value, final String... props) {
        clauses.add(new Clause(value, props, Type.NOT_EQUALS));
        return this;
    }

    /**
     * > 条件, 根据这个条件组装语句为: <br/>
     * (props[0] > value or props[1] > value or ...)
     * 
     * @param value
     *            值
     * @param props
     *            字段属性列表, 可以是多个, 这几个字段之间是or 的关系
     * @return
     */
    public Condition addGreater(final Object value, final String... props) {
        clauses.add(new Clause(value, props, Type.GREATER));
        return this;
    }

    /**
     * >= 条件, 根据这个条件组装语句为: <br/>
     * (props[0] >= value or props[1] >= value or ...)
     * 
     * @param value
     *            值
     * @param props
     *            字段属性列表, 可以是多个, 这几个字段之间是or 的关系
     * @return
     */
    public Condition addGreaterEquals(final Object value, final String... props) {
        clauses.add(new Clause(value, props, Type.GREATER_EQUALS));
        return this;
    }

    /**
     * in 条件, 根据这个条件组装语句为: <br/>
     * (props[0] in (value_1, value_2, ...) or props[1] in (value_1, value_2,
     * ...) or ...)
     * 
     * @param values
     *            in 的值列表, 不能为null
     * @param props
     *            字段属性列表, 可以是多个, 这几个字段之间是or 的关系
     */
    public Condition addIn(final Collection<?> values, final String... props) {
        if (values == null || values.size() == 0) {
            throw new IllegalArgumentException("值列表不能为null");
        }
        clauses.add(new Clause(values, props, Type.IN));
        return this;
    }

    /**
     * not in 条件, 根据这个条件组装语句为: <br/>
     * (props[0] not in (value_1, value_2, ...) or props[1] not in (value_1,
     * value_2, ...) or ...)
     * 
     * @param values
     *            not in 的值列表, 不能为null
     * @param props
     *            字段属性列表, 可以是多个, 这几个字段之间是or 的关系
     */
    public Condition addNotIn(final Collection<?> values, final String... props) {
        if (values == null || values.size() == 0) {
            throw new IllegalArgumentException("值列表不能为null");
        }
        clauses.add(new Clause(values, props, Type.NOT_IN));
        return this;
    }

    /**
     * < 条件, 根据这个条件组装语句为: <br/>
     * (props[0] < value or props[1] < value or ...)
     * 
     * @param value
     *            值
     * @param props
     *            字段属性列表, 可以是多个, 这几个字段之间是or 的关系
     * @return
     */
    public Condition addLess(final Object value, final String... props) {
        clauses.add(new Clause(value, props, Type.LESS));
        return this;
    }

    /**
     * <= 条件, 根据这个条件组装语句为: <br/>
     * (props[0] <= value or props[1] <= value or ...)
     * 
     * @param value
     *            值
     * @param props
     *            字段属性列表, 可以是多个, 这几个字段之间是or 的关系
     * @return
     */
    public Condition addLessEquals(final Object value, final String... props) {
        clauses.add(new Clause(value, props, Type.LESS_EQUALS));
        return this;
    }

    /**
     * 添加自定义的查询条件
     * 
     * @param condition
     *            自定义的查询条件, 开头不需要带 and
     * @return
     */
    public Condition addCustom(final String condition) {
        if (custom == null) {
            custom = new ArrayList<String>();
        }
        custom.add(condition);
        return this;
    }

    /**
     * 添加自定义的参数, 用于在语句中出现的参数注入.<br/>
     * 对于{@link #addLike(String, String...)} 这些方法, 不需要再使用这个方法添加<br/>
     * 不过对于{@link #addCustom(String)}, 如果有带参数, 则需要用这个方法添加参数值
     * 
     * @param value
     *            值
     * @param key
     *            和sql 中的 :key 一致
     * @return
     */
    public Condition addCustomParam(final Object value, final String key) {
        if (customParam == null) {
            customParam = new HashMap<String, Object>();
        }
        customParam.put(key, value);
        return this;
    }

    static class Clause {
        private final Object value;
        private final String[] props;
        private final Type type;

        public Clause(final Object value, final String[] props, final Type type) {
            this.value = value;
            this.props = props;
            this.type = type;
        }

        public Object getValue() {
            return value;
        }

        public String[] getProps() {
            return props;
        }

        public Type getType() {
            return type;
        }
    }

    static enum Type {
        LIKE(" like "),

        NOT_LIKE(" not like "),

        EQUALS(" = "),

        NOT_EQUALS(" != "),

        GREATER(" > "),

        GREATER_EQUALS(" >= "),

        LESS(" < "),

        LESS_EQUALS(" <= "),

        IN(" in "),

        NOT_IN(" not in ");

        private final String operator;

        private Type(final String operator) {
            this.operator = operator;
        }

        public String getOperator() {
            return operator;
        }
    }
}
